#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<ctype.h>
#include<arpa/inet.h>
#include<fcntl.h>
#include<string.h>
#include<sys/select.h>
#include<sys/socket.h>
#define PORT_INT 9854
//基于多路IO复用实现多线程并发通信：select
//思想：一个线程监听所有的文件描述符
int main(void)
{

    int lfd,cfd;
    char buf[1024];

    struct sockaddr_in serv_addr,clid_addr;

    socklen_t clie_addr_len;
    
    int opt=0;
    lfd=socket(AF_INET,SOCK_STREAM,0);
    setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

    memset(&serv_addr,0,sizeof(serv_addr));

    serv_addr.sin_family=AF_INET;
    serv_addr.sin_port=htons(PORT_INT);
    serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);

    bind(lfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
    listen(lfd,128);//同一时间监听的客户端上限数目,能监听的数目最大值是1024（文件描述符）

    //设置监听集合
    fd_set readset,allset;
    FD_ZERO(&allset);
    FD_SET(lfd,&allset);
    int maxfd;
    maxfd=lfd;
    int fdCount;

    while(1)
    {
        readset=allset;
        fdCount=select(maxfd+1,&readset,NULL,NULL,NULL);
        if(fdCount<0)
        {
            perror("select fail!\n");
            exit(1);

        }

    

        if(FD_ISSET(lfd,&readset))
        {
            //说明监听有客户端发出请求的连接
            clie_addr_len=sizeof(clid_addr);
           cfd= accept(lfd,(struct sockaddr*)&clid_addr,clie_addr_len);

           FD_SET(cfd,&allset);

           if(cfd>maxfd)
           {
               maxfd=cfd;
           }

           if(fdCount==1)
           {
               //select 仅返回一个文件描述符，既lfd,仅有一个监听事件发生
               continue;
           }

        }

        //返回的readset集合数据大于1，需要对其进行遍历，看那些文件描述符对应的事件发生了
        for(int i=lfd+1;i<=maxfd;i++ )
        {
            if(FD_ISSET(i,&readset))
            {
                //说明监听到所有文件描述符号中有，读事件发生
               int ret=read(i,buf,sizeof(buf));//相当从cfd中读数据
               if(ret==0)
               {
                   //相当于客户端关闭了写端，service侧停止read
                   close(i);
                   FD_CLR(i,&allset);//将该文件描述符从监听集合中剔除
               }else if(ret==-1)
               {
                   perror("read fail!\n");

               }

               for(int j=0;j<ret;j++)
               {
                   buf[j]=toupper(buf[j]);
               }

               write(i,buf,ret);
               write(STDOUT_FILENO,buf,ret);

            }
        }

    }


        return 0;
    

}
